/*! touchjs v0.2.14  2014-08-05 */
'use strict';

function cslog() {

	if (true) {
		console.log.apply(window, arguments);
	}
}


(function(root, factory) {
    if (typeof define === 'function' && (define.amd || define.cmd)) {
        define(factory); //Register as a module.
    } else {
        root.touch = factory();
    }
}(this, function() {

	var utils = {};

	utils.PCevts = {
		'touchstart': 'mousedown',
		'touchmove': 'mousemove',
		'touchend': 'mouseup',
		'touchcancel': 'mouseout',

		// 作为测试，手动添加；
		// 经过测试，touch.on(elem, 'tap', handler), 在pc 版和手机版都能执行；
		// 当没有添加时，pc 版不会执行 tap。
		// 我觉得还是应该加上才对，非常方便测试和使用。
		'tap': 'click',
		'doubletap': 'dblclick',
	};
	
	// true 表示支持手势操作，也即触摸屏；
	utils.hasTouch = ('ontouchstart' in window);

	utils.getType = function(obj) {
		return Object.prototype.toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
	};

	utils.getSelector = function(el) {
		if (el.id) {
			return "#" + el.id;
		}
		if (el.className) {
			var cns = el.className.split(/\s+/);
			return "." + cns.join(".");
		} else if (el === document) {
			return "body";
		} else {
			return el.tagName.toLowerCase();
		}
	};

	utils.matchSelector = function(target, selector) {
		return target.webkitMatchesSelector(selector);
	};

	utils.getEventListeners = function(el) {
		return el.listeners;
	};

	utils.getPCevts = function(evt) {
		return this.PCevts[evt] || evt;
	};

	// n. 	软熔焊接; 文档重整，页面重排(调整文本在计算机屏幕上的显示密度);
	utils.forceReflow = function() {
		var tempDivID = "reflowDivBlock";
		var domTreeOpDiv = document.getElementById(tempDivID);
		if (!domTreeOpDiv) {
			domTreeOpDiv = document.createElement("div");
			domTreeOpDiv.id = tempDivID;
			document.body.appendChild(domTreeOpDiv);
		}
		var parentNode = domTreeOpDiv.parentNode;
		var nextSibling = domTreeOpDiv.nextSibling;
		parentNode.removeChild(domTreeOpDiv);
		parentNode.insertBefore(domTreeOpDiv, nextSibling);
	};

	utils.simpleClone = function(obj) {
		return Object.create(obj);
	};

	utils.getPosOfEvent = function(ev) {
		if (this.hasTouch) {
			var posi = [];
			var src = null;

			for (var t = 0, len = ev.touches.length; t < len; t++) {
				src = ev.touches[t];
				posi.push({
					x: src.pageX,
					y: src.pageY
				});
			}
			return posi;
		} else {
			return [{
				x: ev.pageX,
				y: ev.pageY
			}];
		}
	};

	utils.getDistance = function(pos1, pos2) {
		var x = pos2.x - pos1.x,
			y = pos2.y - pos1.y;
		return Math.sqrt((x * x) + (y * y));
	};

	utils.getFingers = function(ev) {
		return ev.touches ? ev.touches.length : 1;
	};

	utils.calScale = function(pstart, pmove) {
		if (pstart.length >= 2 && pmove.length >= 2) {
			var disStart = this.getDistance(pstart[1], pstart[0]);
			var disEnd = this.getDistance(pmove[1], pmove[0]);

			return disEnd / disStart;
		}
		return 1;
	};

	utils.getAngle = function(p1, p2) {
		return Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
	};

	utils.getAngle180 = function(p1, p2) {
		var agl = Math.atan((p2.y - p1.y) * -1 / (p2.x - p1.x)) * (180 / Math.PI);
		return (agl < 0 ? (agl + 180) : agl);
	};

	utils.getDirectionFromAngle = function(agl) {
		var directions = {
			up: agl < -45 && agl > -135,
			down: agl >= 45 && agl < 135,
			left: agl >= 135 || agl <= -135,
			right: agl >= -45 && agl <= 45
		};
		for (var key in directions) {
			if (directions[key]) return key;
		}
		return null;
	};

	utils.getXYByElement = function(el) {
		var left = 0,
			top = 0;

		while (el.offsetParent) {
			left += el.offsetLeft;
			top += el.offsetTop;
			el = el.offsetParent;
		}
		return {
			left: left,
			top: top
		};
	};

	utils.reset = function() {
		startEvent = moveEvent = endEvent = null;
		__tapped = __touchStart = startSwiping = startPinch = false;
		startDrag = false;
		pos = {};
		__rotation_single_finger = false;
	};

	utils.isTouchMove = function(ev) {
		return (ev.type === 'touchmove' || ev.type === 'mousemove');
	};

	utils.isTouchEnd = function(ev) {
		return (ev.type === 'touchend' || ev.type === 'mouseup' || ev.type === 'touchcancel');
	};

	utils.env = (function() {
		var os = {}, ua = navigator.userAgent,
			android = ua.match(/(Android)[\s\/]+([\d\.]+)/),
			ios = ua.match(/(iPad|iPhone|iPod)\s+OS\s([\d_\.]+)/),
			wp = ua.match(/(Windows\s+Phone)\s([\d\.]+)/),
			isWebkit = /WebKit\/[\d.]+/i.test(ua),
			isSafari = ios ? (navigator.standalone ? isWebkit : (/Safari/i.test(ua) && !/CriOS/i.test(ua) && !/MQQBrowser/i.test(ua))) : false;
		if (android) {
			os.android = true;
			os.version = android[2];
		}
		if (ios) {
			os.ios = true;
			os.version = ios[2].replace(/_/g, '.');
			os.ios7 = /^7/.test(os.version);
			if (ios[1] === 'iPad') {
				os.ipad = true;
			} else if (ios[1] === 'iPhone') {
				os.iphone = true;
				os.iphone5 = screen.height == 568;
			} else if (ios[1] === 'iPod') {
				os.ipod = true;
			}
		}
		if (wp) {
			os.wp = true;
			os.version = wp[2];
			os.wp8 = /^8/.test(os.version);
		}
		if (isWebkit) {
			os.webkit = true;
		}
		if (isSafari) {
			os.safari = true;
		}
		return os;
	})();

	/** 底层事件绑定/代理支持  */
	var engine = {
		proxyid: 0,
		proxies: [],
		trigger: function(el, evt, detail) {

			detail = detail || {};
			var e, opt = {
					bubbles: true,
					cancelable: true,
					detail: detail
				};

			try {
				if (typeof CustomEvent !== 'undefined') {
					e = new CustomEvent(evt, opt);
					if (el) {
						el.dispatchEvent(e);
					}
				} else {
					e = document.createEvent("CustomEvent");
					e.initCustomEvent(evt, true, true, detail);
					if (el) {
						el.dispatchEvent(e);
					}
				}
			} catch (ex) {
				console.warn("Touch.js is not supported by environment.");
			}
		},
		bind: function(el, evt, handler) {
			el.listeners = el.listeners || {};
			if (!el.listeners[evt]) {
				el.listeners[evt] = [handler];
			} else {
				el.listeners[evt].push(handler);
			}
			var proxy = function(e) {
				if (utils.env.ios7) {
					utils.forceReflow();
				}
				
				// 就产生了递归，所以不能使用 JSON.stringify() 来打印；
				e.originEvent = e;

				// touchstart/touchmove/touchend 时，ev.detail 都是 0；
				for (var p in e.detail) {
					if (p !== 'type') {
						e[p] = e.detail[p];
					}
				}
				e.startRotate = function() {
					__rotation_single_finger = true;
				};
				var returnValue = handler.call(e.target, e);
				if (typeof returnValue !== "undefined" && !returnValue) {
					e.stopPropagation();
					e.preventDefault();
				}
			};
			handler.proxy = handler.proxy || {};
			if (!handler.proxy[evt]) {
				handler.proxy[evt] = [this.proxyid++];
			} else {
				handler.proxy[evt].push(this.proxyid++);
			}

			this.proxies.push(proxy);

			if (el.addEventListener) {
				el.addEventListener(evt, proxy, false);
			}
		},
		// 要想移除事件处理函数，必须是具名的，因为匿名的无法删除；
		unbind: function(el, evt, handler) {
			if (!handler) {
				var handlers = el.listeners[evt];
				if (handlers && handlers.length) {
					handlers.forEach(function(handler) {
						el.removeEventListener(evt, handler, false);
					});
				}
			} else {
				var proxyids = handler.proxy[evt];
				if (proxyids && proxyids.length) {
					proxyids.forEach(function(proxyid) {
						if (el.removeEventListener) {
							// 疑似 bug：下面这行的 this 应该是 window；
							// 我在本机测试的，就是window，导致 this.proxies 和 this.proxyid 都是 undefined 而报错；
							el.removeEventListener(evt, this.proxies[this.proxyid], false);
						}
					});
				}
			}
		},
		delegate: function(el, evt, sel, handler) {
			var proxy = function(e) {
				var target, returnValue;
				e.originEvent = e;
				for (var p in e.detail) {
					if (p !== 'type') {
						e[p] = e.detail[p];
					}
				}
				e.startRotate = function() {
					__rotation_single_finger = true;
				};
				var integrateSelector = utils.getSelector(el) + " " + sel;
				var match = utils.matchSelector(e.target, integrateSelector);
				var ischild = utils.matchSelector(e.target, integrateSelector + " " + e.target.nodeName);
				if (!match && ischild) {
					if (utils.env.ios7) {
						utils.forceReflow();
					}
					target = e.target;
					while (!utils.matchSelector(target, integrateSelector)) {
						target = target.parentNode;
					}
					returnValue = handler.call(e.target, e);
					if (typeof returnValue !== "undefined" && !returnValue) {
						e.stopPropagation();
						e.preventDefault();
					}
				} else {
					if (utils.env.ios7) {
						utils.forceReflow();
					}
					if (match || ischild) {
						returnValue = handler.call(e.target, e);
						if (typeof returnValue !== "undefined" && !returnValue) {
							e.stopPropagation();
							e.preventDefault();
						}
					}
				}
			};
			handler.proxy = handler.proxy || {};
			if (!handler.proxy[evt]) {
				handler.proxy[evt] = [this.proxyid++];
			} else {
				handler.proxy[evt].push(this.proxyid++);
			}
			this.proxies.push(proxy);
			el.listeners = el.listeners || {};
			if (!el.listeners[evt]) {
				el.listeners[evt] = [proxy];
			} else {
				el.listeners[evt].push(proxy);
			}
			if (el.addEventListener) {
				el.addEventListener(evt, proxy, false);
			}
		},
		undelegate: function(el, evt, sel, handler) {
			if (!handler) {
				var listeners = el.listeners[evt];
				listeners.forEach(function(proxy) {
					el.removeEventListener(evt, proxy, false);
				});
			} else {
				var proxyids = handler.proxy[evt];
				if (proxyids.length) {
					proxyids.forEach(function(proxyid) {
						if (el.removeEventListener) {
							el.removeEventListener(evt, this.proxies[this.proxyid], false);
						}
					});
				}
			}
		}
	};

	var config = {
		tap: true,
		doubleTap: true,
		tapMaxDistance: 10,// 单击事件，允许最大滑动距离；
		hold: true,
		tapTime: 200,// 单击事件的判断；
		holdTime: 650,// 长按事件的判断；
		maxDoubleTapInterval: 300,// 双击事件，两次点击之间最大间隔时长；
		swipe: true,
		// swipeTime: 300,// 最小滑动时间；滑动持续时间如果小于这个值，不视为滑动；另外，看代码发现，最大滑动持续时间是这个值 +50；
		swipeMinDistance: 18,// 视为滑动事件的最小滑动距离；
		swipeFactor: 5,
		drag: true,
		pinch: true,
		minScaleRate: 0,
		minRotationAngle: 0
	};

	var smrEventList = {
		// key：从这里，到 CLICK（含），这个文件中都没有出现过第二次；
		// 
		TOUCH_START: 'touchstart',
		TOUCH_MOVE: 'touchmove',
		TOUCH_END: 'touchend',
		TOUCH_CANCEL: 'touchcancel',
		MOUSE_DOWN: 'mousedown',
		MOUSE_MOVE: 'mousemove',
		MOUSE_UP: 'mouseup',
		CLICK: 'click',// 此文件中没有用过 CLICK；就算是 click 也只在这里出现一次；
		PINCH_START: 'pinchstart',
		PINCH_END: 'pinchend',
		PINCH: 'pinch',// 捏; 掐; 拧; 夹;
		PINCH_IN: 'pinchin',
		PINCH_OUT: 'pinchout',
		ROTATION_LEFT: 'rotateleft',
		ROTATION_RIGHT: 'rotateright',
		ROTATION: 'rotate',
		SWIPE_START: 'swipestart',
		SWIPING: 'swiping',
		SWIPE_END: 'swipeend',
		SWIPE_LEFT: 'swipeleft',
		SWIPE_RIGHT: 'swiperight',
		SWIPE_UP: 'swipeup',
		SWIPE_DOWN: 'swipedown',
		SWIPE: 'swipe',
		DRAG: 'drag',
		DRAGSTART: 'dragstart',
		DRAGEND: 'dragend',
		HOLD: 'hold',// 此文件中没有用过 HOLD；
		TAP: 'tap',
		DOUBLE_TAP: 'doubletap'
	};

	/** 手势识别 */
	var pos = {
		start: null,
		move: null,
		end: null
	};

	var startTime = 0;
	var fingers = 0;
	var startEvent = null;
	var moveEvent = null;
	var endEvent = null;
	var startSwiping = false;
	var startPinch = false;
	var startDrag = false;

	var __offset = {};
	// mousedown 和 touchstart 时设置为 true；
	var __touchStart = false;
	var __holdTimer = null;
	var __tapped = false;
	var __lastTapEndTime = null;
	var __tapTimer = null;

	var __scale_last_rate = 1;
	var __rotation_single_finger = false;
	var __rotation_single_start = [];
	var __initial_angle = 0;
	var __rotation = 0;

	var __prev_tapped_end_time = 0;
	var __prev_tapped_pos = null;

	var gestures = {
		getAngleDiff: function(currentPos) {
			var diff = parseInt(__initial_angle - utils.getAngle180(currentPos[0], currentPos[1]), 10);
			var count = 0;

			while (Math.abs(diff - __rotation) > 90 && count++ < 50) {
				if (__rotation < 0) {
					diff -= 180;
				} else {
					diff += 180;
				}
			}
			__rotation = parseInt(diff, 10);
			return __rotation;
		},
		// 2 个及以上手指的事件；每个像素都会执行；
		// case 'touchmove':
		// case 'mousemove':
		// 另外，结束时也会执行；
		// case 'touchend':
		// case 'touchcancel':
		// case 'mouseup':
		// case 'mouseout':
		pinch: function(ev) {
			var el = ev.target;
			if (config.pinch) {
				// mousedown 和 touchstart 时会设置为 true，所以这里可以忽略；
				if (!__touchStart) return;

				// 对于开始执行这个方法：if (utils.getFingers(ev) >= 2) 满足这个条件才执行 pinch() 方法，所以下面的判断可以忽略了；
				// 对于结束执行这个方法：
				if (utils.getFingers(ev) < 2) {
					if (!utils.isTouchEnd(ev)) return;
				}

				var scale = utils.calScale(pos.start, pos.move);
				var rotation = this.getAngleDiff(pos.move);

				var eventObj = {
					type: '',
					originEvent: ev,
					scale: scale,
					rotation: rotation,
					direction: (rotation > 0 ? 'right' : 'left'),
					fingersCount: utils.getFingers(ev)
				};
				// 第一次执行 pinch() 方法时设置为 start；
				// 最后一次设置为 end；
				// 中间的部分全部是 move；
				if (!startPinch) {
					startPinch = true;
					eventObj.fingerStatus = "start";
					engine.trigger(el, smrEventList.PINCH_START, eventObj);
				} else if (utils.isTouchMove(ev)) {
					eventObj.fingerStatus = "move";
					engine.trigger(el, smrEventList.PINCH, eventObj);
				} else if (utils.isTouchEnd(ev)) {
					eventObj.fingerStatus = "end";
					engine.trigger(el, smrEventList.PINCH_END, eventObj);
					utils.reset();
				}

				if (Math.abs(1 - scale) > config.minScaleRate) {
					var scaleEv = utils.simpleClone(eventObj);

					//手势放大, 触发pinchout事件
					var scale_diff = 0.00000000001; //防止touchend的scale与__scale_last_rate相等，不触发事件的情况。
					if (scale > __scale_last_rate) {
						__scale_last_rate = scale - scale_diff;
						engine.trigger(el, smrEventList.PINCH_OUT, scaleEv, false);
					} //手势缩小,触发pinchin事件
					else if (scale < __scale_last_rate) {
						__scale_last_rate = scale + scale_diff;
						engine.trigger(el, smrEventList.PINCH_IN, scaleEv, false);
					}

					if (utils.isTouchEnd(ev)) {
						__scale_last_rate = 1;
					}
				}

				if (Math.abs(rotation) > config.minRotationAngle) {
					var rotationEv = utils.simpleClone(eventObj),
						eventType;

					eventType = rotation > 0 ? smrEventList.ROTATION_RIGHT : smrEventList.ROTATION_LEFT;
					engine.trigger(el, eventType, rotationEv, false);
					engine.trigger(el, smrEventList.ROTATION, eventObj);
				}

			}
		},
		rotateSingleFinger: function(ev) {
			var el = ev.target;
			if (__rotation_single_finger && utils.getFingers(ev) < 2) {
				if (!pos.move) return;
				if (__rotation_single_start.length < 2) {
					var docOff = utils.getXYByElement(el);

					__rotation_single_start = [{
							x: docOff.left + el.offsetWidth / 2,
							y: docOff.top + el.offsetHeight / 2
						},
						pos.move[0]
					];
					__initial_angle = parseInt(utils.getAngle180(__rotation_single_start[0], __rotation_single_start[1]), 10);
				}
				var move = [__rotation_single_start[0], pos.move[0]];
				var rotation = this.getAngleDiff(move);
				var eventObj = {
					type: '',
					originEvent: ev,
					rotation: rotation,
					direction: (rotation > 0 ? 'right' : 'left'),
					fingersCount: utils.getFingers(ev)
				};
				if (utils.isTouchMove(ev)) {
					eventObj.fingerStatus = "move";
				} else if (utils.isTouchEnd(ev) || ev.type === 'mouseout') {
					eventObj.fingerStatus = "end";
					engine.trigger(el, smrEventList.PINCH_END, eventObj);
					utils.reset();
				}
				var eventType = rotation > 0 ? smrEventList.ROTATION_RIGHT : smrEventList.ROTATION_LEFT;
				engine.trigger(el, eventType, eventObj);
				engine.trigger(el, smrEventList.ROTATION, eventObj);
			}
		},


		// 只会在一个手指操作时执行这个方法。
		// 意味着每个像素都会执行一次；
		// case 'touchmove':
		// case 'mousemove':
		// 另外：
		// case 'touchend':
		// case 'touchcancel':
		// case 'mouseup':
		// case 'mouseout':
		// 也会根据条件来执行；
		swipe: function(ev) {
			cslog('swipe');
			ajaxTouchInsert('swipe', '1');

			var el = ev.target;
			if (!__touchStart || !pos.move || utils.getFingers(ev) > 1) {
				return;
			}

			var now = Date.now();
			var touchTime = now - startTime;
			var distance = utils.getDistance(pos.start[0], pos.move[0]);
			var position = {
				x: pos.move[0].x - __offset.left,
				y: pos.move[0].y - __offset.top
			};
			var angle = utils.getAngle(pos.start[0], pos.move[0]);
			var direction = utils.getDirectionFromAngle(angle);

			// touchSecond 和 factor 有什么用吗~
			var touchSecond = touchTime / 1000;
			// swipeFactor 就这里用到一次，相当于没意义吧；
			var factor = ((10 - config.swipeFactor) * 10 * touchSecond * touchSecond);
			var eventObj = {
				type: smrEventList.SWIPE,
				originEvent: ev,
				position: position,
				direction: direction,
				distance: distance,
				distanceX: pos.move[0].x - pos.start[0].x,
				distanceY: pos.move[0].y - pos.start[0].y,
				x: pos.move[0].x - pos.start[0].x,
				y: pos.move[0].y - pos.start[0].y,
				angle: angle,
				duration: touchTime,
				fingersCount: utils.getFingers(ev),
				factor: factor
			};

			if (config.swipe) {
				var swipeTo = function() {
					var elt = smrEventList;
					switch (direction) {
						case 'up':
							engine.trigger(el, elt.SWIPE_UP, eventObj);
							break;
						case 'down':
							engine.trigger(el, elt.SWIPE_DOWN, eventObj);
							break;
						case 'left':
							engine.trigger(el, elt.SWIPE_LEFT, eventObj);
							break;
						case 'right':
							engine.trigger(el, elt.SWIPE_RIGHT, eventObj);
							break;
					}
				};

				if (!startSwiping) {
					cslog('!startSwiping');
					eventObj.fingerStatus = eventObj.swipe = 'start';
					startSwiping = true;
					engine.trigger(el, smrEventList.SWIPE_START, eventObj);
				} else if (utils.isTouchMove(ev)) {
					cslog('utils.isTouchMove(ev)');
					cslog('-------- touchTime');
					cslog(touchTime);

					eventObj.fingerStatus = eventObj.swipe = 'move';
					engine.trigger(el, smrEventList.SWIPING, eventObj);

					if (touchTime > config.swipeTime && touchTime < config.swipeTime + 50 && distance > config.swipeMinDistance) {
						swipeTo();
						engine.trigger(el, smrEventList.SWIPE, eventObj, false);
					}
				} else if (utils.isTouchEnd(ev) || ev.type === 'mouseout') {
					cslog('utils.isTouchEnd(ev)');
					eventObj.fingerStatus = eventObj.swipe = 'end';
					engine.trigger(el, smrEventList.SWIPE_END, eventObj);

					if (config.swipeTime > touchTime && distance > config.swipeMinDistance) {
						swipeTo();
						engine.trigger(el, smrEventList.SWIPE, eventObj, false);
					}
				}
			}

			if (config.drag) {
				if (!startDrag) {
					eventObj.fingerStatus = eventObj.swipe = 'start';
					startDrag = true;
					engine.trigger(el, smrEventList.DRAGSTART, eventObj);
				} else if (utils.isTouchMove(ev)) {
					eventObj.fingerStatus = eventObj.swipe = 'move';
					engine.trigger(el, smrEventList.DRAG, eventObj);
				} else if (utils.isTouchEnd(ev)) {
					eventObj.fingerStatus = eventObj.swipe = 'end';
					engine.trigger(el, smrEventList.DRAGEND, eventObj);
				}

			}
		},
		// 单击和双击；
		// 只用到一次：
		// case 'touchend':
		// case 'touchcancel':
		// case 'mouseup':
		// case 'mouseout':
		tap: function(ev) {
			var el = ev.target;
			if (config.tap) {
				var now = Date.now();
				var touchTime = now - startTime;
				var distance = utils.getDistance(pos.start[0], pos.move ? pos.move[0] : pos.start[0]);

				clearTimeout(__holdTimer);
				var isDoubleTap = (function() {
					if (__prev_tapped_pos && config.doubleTap && (startTime - __prev_tapped_end_time) < config.maxDoubleTapInterval) {
						var doubleDis = utils.getDistance(__prev_tapped_pos, pos.start[0]);
						// 两次点击的距离
						if (doubleDis < 16) return true;
					}
					return false;
				})();

				if (isDoubleTap) {
					clearTimeout(__tapTimer);
					engine.trigger(el, smrEventList.DOUBLE_TAP, {
						type: smrEventList.DOUBLE_TAP,
						originEvent: ev,
						position: pos.start[0]
					});
					return;
				}

				// 单击事件，运行一定范围的滑动，超过了则视为 swipe，而不再视为 tap；
				if (config.tapMaxDistance < distance) return;

				// 单击事件，点击的持续时间，按分界点分为单击和长按--或者分为短击和长击；
				if (config.holdTime > touchTime && utils.getFingers(ev) <= 1) {
					__tapped = true;
					__prev_tapped_end_time = now;
					__prev_tapped_pos = pos.start[0];
					__tapTimer = setTimeout(function() {
							engine.trigger(el, smrEventList.TAP, {
								type: smrEventList.TAP,
								originEvent: ev,
								fingersCount: utils.getFingers(ev),
								position: __prev_tapped_pos
							});
						},
						config.tapTime);
				}
			}
		},
		// 长按；
		hold: function(ev) {
			var el = ev.target;
			if (config.hold) {
				clearTimeout(__holdTimer);

				__holdTimer = setTimeout(function() {
						if (!pos.start) return;

						// 这个 hold() 方法只在 mousedown和 touchstart 时执行一次；
						// 如何会有 pos.move 呢？这是定时器内执行的，执行时可能会有 pos.move 了。
						var distance = utils.getDistance(pos.start[0], pos.move ? pos.move[0] : pos.start[0]);
						// 如果超过了 tap 指定的距离，不再视为 tap，而视为 swipe 了。
						if (config.tapMaxDistance < distance) return;

						if (!__tapped) {
							cslog('触发元素的 hold 事件');
							engine.trigger(el, "hold", {
								type: 'hold',
								originEvent: ev,
								fingersCount: utils.getFingers(ev),
								position: pos.start[0]
							});
						}
					},
					config.holdTime);
			}
		}
	};

	// gestures.pinch(ev),
	// gestures.rotateSingleFinger(ev),
	// gestures.swipe(ev),
	// gestures.tap(ev),
	// gestures.hold(ev),
	// 上面几个方法中参数 ev，就是下面的 handlerOriginEvent 方法的参数 ev；
	var handlerOriginEvent = function(ev) {

		var el = ev.target;
		switch (ev.type) {
			case 'touchstart':
			case 'mousedown':
				cslog('---- touchstart|mousedown ----');
				__rotation_single_start = [];
				__touchStart = true;
				if (!pos.start || pos.start.length < 2) {
					pos.start = utils.getPosOfEvent(ev);
				}
				if (utils.getFingers(ev) >= 2) {
					__initial_angle = parseInt(utils.getAngle180(pos.start[0], pos.start[1]), 10);
				}

				startTime = Date.now();
				startEvent = ev;
				__offset = {};

				var box = el.getBoundingClientRect();
				var docEl = document.documentElement;
				__offset = {
					top: box.top + (window.pageYOffset || docEl.scrollTop) - (docEl.clientTop || 0),
					left: box.left + (window.pageXOffset || docEl.scrollLeft) - (docEl.clientLeft || 0)
				};

				gestures.hold(ev);
				break;
			case 'touchmove':
			case 'mousemove':
				cslog('---- touchmove|mousemove ----');
				// cslog('__rotation_single_finger', __rotation_single_finger);
				// cslog('ev.target', ev.target);
				if (!__touchStart || !pos.start) return;
				pos.move = utils.getPosOfEvent(ev);
				if (utils.getFingers(ev) >= 2) {
					gestures.pinch(ev);
				} else if (__rotation_single_finger) {
					gestures.rotateSingleFinger(ev);
				} else {
					gestures.swipe(ev);
				}
				break;
			case 'touchend':
			case 'touchcancel':
			case 'mouseup':
			case 'mouseout':
				cslog('---- touchend|touchcancel|mouseup|mouseout ----');
				// 两个及更多的手指：每一个手指单独离开都会执行这里，也就是 touchend。

				if (!__touchStart) return;
				endEvent = ev;

				if (startPinch) {
					gestures.pinch(ev);
				} else if (__rotation_single_finger) {
					gestures.rotateSingleFinger(ev);
				} else if (startSwiping) {
					gestures.swipe(ev);
				} else {
					gestures.tap(ev);
				}

				utils.reset();
				__initial_angle = 0;
				__rotation = 0;
				if (ev.touches && ev.touches.length === 1) {
					__touchStart = true;
					__rotation_single_finger = true;
				}
				break;
		}
	};

	// 2 个参数：('div/.box/#box', {e1: func1, e2: func2});
	// 3 个参数：('div/.box/#box', 'e1 e2 ...', func);
	// 4 个参数：事件委托机制(ele1, 'e1 e2 ...', ele2, func)。
	var _on = function() {

		var evts, handler, evtMap, sel, args = arguments;
		if (args.length < 2 || args > 4) {
			return console.error("unexpected arguments!");
		}
		var els = utils.getType(args[0]) === 'string' ? document.querySelectorAll(args[0]) : args[0];
		els = els.length ? Array.prototype.slice.call(els) : [els];
		//事件绑定
		if (args.length === 3 && utils.getType(args[1]) === 'string') {
			evts = args[1].split(" ");
			handler = args[2];
			evts.forEach(function(evt) {
				if (!utils.hasTouch) {
					evt = utils.getPCevts(evt);
				}
				els.forEach(function(el) {
					engine.bind(el, evt, handler);
				});
			});
			return;
		}

		function evtMapDelegate(evt) {
			if (!utils.hasTouch) {
				evt = utils.getPCevts(evt);
			}
			els.forEach(function(el) {
				engine.delegate(el, evt, sel, evtMap[evt]);
			});
		}
		//mapEvent delegate
		if (args.length === 3 && utils.getType(args[1]) === 'object') {
			evtMap = args[1];
			sel = args[2];
			for (var evt1 in evtMap) {
				evtMapDelegate(evt1);
			}
			return;
		}

		function evtMapBind(evt) {
			if (!utils.hasTouch) {
				evt = utils.getPCevts(evt);
			}
			els.forEach(function(el) {
				engine.bind(el, evt, evtMap[evt]);
			});
		}

		//mapEvent bind
		if (args.length === 2 && utils.getType(args[1]) === 'object') {
			evtMap = args[1];
			for (var evt2 in evtMap) {
				evtMapBind(evt2);
			}
			return;
		}

		//兼容factor config
		if (args.length === 4 && utils.getType(args[2]) === "object") {
			evts = args[1].split(" ");
			handler = args[3];
			evts.forEach(function(evt) {
				if (!utils.hasTouch) {
					evt = utils.getPCevts(evt);
				}
				els.forEach(function(el) {
					engine.bind(el, evt, handler);
				});
			});
			return;
		}

		//事件代理
		if (args.length === 4) {
			var el = els[0];
			evts = args[1].split(" ");
			sel = args[2];
			handler = args[3];
			evts.forEach(function(evt) {
				if (!utils.hasTouch) {
					evt = utils.getPCevts(evt);
				}
				engine.delegate(el, evt, sel, handler);
			});
			return;
		}
	};

	var _off = function() {
		var evts, handler;
		var args = arguments;
		if (args.length < 1 || args.length > 4) {
			return console.error("unexpected arguments!");
		}
		var els = utils.getType(args[0]) === 'string' ? document.querySelectorAll(args[0]) : args[0];
		els = els.length ? Array.prototype.slice.call(els) : [els];

		if (args.length === 1 || args.length === 2) {
			els.forEach(function(el) {
				evts = args[1] ? args[1].split(" ") : Object.keys(el.listeners);
				if (evts.length) {
					evts.forEach(function(evt) {
						if (!utils.hasTouch) {
							evt = utils.getPCevts(evt);
						}
						engine.unbind(el, evt);
						engine.undelegate(el, evt);
					});
				}
			});
			return;
		}

		if (args.length === 3 && utils.getType(args[2]) === 'function') {
			handler = args[2];
			els.forEach(function(el) {
				evts = args[1].split(" ");
				evts.forEach(function(evt) {
					if (!utils.hasTouch) {
						evt = utils.getPCevts(evt);
					}
					engine.unbind(el, evt, handler);
				});
			});
			return;
		}

		if (args.length === 3 && utils.getType(args[2]) === 'string') {
			var sel = args[2];
			els.forEach(function(el) {
				evts = args[1].split(" ");
				evts.forEach(function(evt) {
					if (!utils.hasTouch) {
						evt = utils.getPCevts(evt);
					}
					engine.undelegate(el, evt, sel);
				});
			});
			return;
		}

		if (args.length === 4) {
			handler = args[3];
			els.forEach(function(el) {
				evts = args[1].split(" ");
				evts.forEach(function(evt) {
					if (!utils.hasTouch) {
						evt = utils.getPCevts(evt);
					}
					engine.undelegate(el, evt, sel, handler);
				});
			});
			return;
		}
	};

	var _dispatch = function(el, evt, detail) {
		cslog('---- arguments', arguments);
		var args = arguments;
		if (!utils.hasTouch) {
			evt = utils.getPCevts(evt);
		}
		var els = utils.getType(args[0]) === 'string' ? document.querySelectorAll(args[0]) : args[0];
		els = els.length ? Array.prototype.call(els) : [els];

		els.forEach(function(el) {
			engine.trigger(el, evt, detail);
		});
	};

    //init gesture
    function init() {

        var mouseEvents = 'mouseup mousedown mousemove mouseout',
            touchEvents = 'touchstart touchmove touchend touchcancel';
        var bindingEvents = utils.hasTouch ? touchEvents : mouseEvents;
        bindingEvents.split(" ").forEach(function(evt) {
            document.addEventListener(evt, handlerOriginEvent, false);
        });
    }

    init();

    var exports = {};

    exports.on = exports.bind = exports.live = _on;
    exports.off = exports.unbind = exports.die = _off;
    exports.config = config;
    // exports.trigger = _dispatch;

    return exports;
}));


/*

在 gestures.pinch(ev) 内触发的可自定义事件：
  touch.on(el, 'pinchstart', handler);
  touch.on(el, 'pinch', handler);
  touch.on(el, 'pinchend', handler);
  缩放
  touch.on(el, 'pinchout', handler);// 放大；
  touch.on(el, 'pinchin', handler);// 缩小；
  旋转
  touch.on(el, 'rotate', handler);
  touch.on(el, 'rotateleft', handler);
  touch.on(el, 'rotateright', handler);
在 gestures.rotateSingleFinger(ev) 内触发的可自定义事件：
  touch.on(el, 'pinchend', handler);
  touch.on(el, 'rotate', handler);
  touch.on(el, 'rotateleft', handler);
  touch.on(el, 'rotateright', handler);
在 gestures.swipe(ev) 内触发的可自定义事件：
  touch.on(el, 'swipestart', handler);
  touch.on(el, 'swiping', handler);
  touch.on(el, 'swipeleft', handler);
  touch.on(el, 'swiperight', handler);
  touch.on(el, 'swipeup', handler);
  touch.on(el, 'swipedown', handler);
  touch.on(el, 'swipe', handler);
  touch.on(el, 'swipeend', handler);
  拖拽
  touch.on(el, 'drag', handler);
  touch.on(el, 'dragstart', handler);
  touch.on(el, 'dragend', handler);
在 gestures.tap(ev) 内触发的可自定义事件：
  touch.on(el, 'doubletap', handler);
  touch.on(el, 'tap', handler);
在 gestures.hold(ev) 内触发的可自定义事件：
  touch.on(el, 'hold', handler);



touch.config(config) //对手势事件库进行全局配置。
功能描述：对手势事件库进行全局配置。
参数描述：
{    
	tap: true,                  // tap类事件开关, 默认为true
	doubleTap: true,        	// doubleTap事件开关， 默认为true
	hold: true,                 // hold事件开关, 默认为true
	holdTime: 650,           	// hold时间长度
	swipe: true,                // swipe事件开关
	swipeTime: 300,             // 触发swipe事件的最大时长
	swipeMinDistance: 18,   	// swipe移动最小距离
	swipeFactor: 5,             // 加速因子, 值越大变化速率越快
	drag: true,                 // drag事件开关
	pinch: true,                // pinch类事件开关
}


事件代理
touch.on( delegateElement, types, selector, callback );
参数描述：
参数			类型				描述
delegateElement	element或string		事件代理元素或选择器
types			string				手势事件的类型, 可接受多个事件以空格分开；支持原生事件的透传。
selector		string				代理子元素选择器,
callback		function			事件处理函数，如需了解手势库支持的新属性


解除事件代理、解除事件绑定
touch.off( delegateElement, types, selector, callback )//解除某元素上的事件代理。
touch.off( element, types, callback )//解除某元素上的事件绑定，根据参数区分事件绑定和事件代理。


触发事件
touch.trigger(element, type);
功能描述：触发某个元素上的某事件。
参数描述：同上 



基本事件：
touchstart   //手指刚接触屏幕时触发
touchmove    //手指在屏幕上移动时触发
touchend     //手指从屏幕上移开时触发
touchcancel  //触摸过程被系统取消时触发（少用）


事件绑定（常用，重要）
touch.on( element, types, callback );
功能描述：事件绑定方法，根据参数区分事件绑定和事件代理。
参数		类型				描述
element		element或string		元素对象、选择器
types		string				事件的类型（多为手势事件），可接受多个事件以空格分开；支持原生事件的透传
callback	function			事件处理函数, 移除函数与绑定函数必须为同一引用;

部分手势事件
分类			参数			描述
缩放			pinchstart		缩放手势起点
				pinchend		缩放手势终点
				pinch			缩放手势
				pinchin			收缩
				pinchout		放大
旋转			rotateleft		向左旋转
				rotateright		向右旋转
				rotate			旋转
滑动			swipestart		滑动手势起点
				swiping			滑动中
				swipeend		滑动手势终点
				swipeleft		向左滑动
				swiperight		向右滑动
				swipeup			向上滑动
				swipedown		向下滑动
				swipe			滑动
拖动开始		dragstart		拖动屏幕
拖动			drag			拖动手势
拖动结束		dragend			拖动屏幕
拖动			drag			拖动手势
长按			hold			长按屏幕
敲击			tap				单击屏幕
				doubletap		双击屏幕



部分事件处理函数
属性			描述
originEvent		触发某事件的原生对象
type			事件的名称
rotation		旋转角度
scale			缩放比例
direction		操作的方向属性
fingersCount	操作的手势数量
position		相关位置信息, 不同的操作产生不同的位置信息
distance		swipe类两点之间的位移
distanceX, x	手势事件x方向的位移值, 向左移动时为负数
distanceY, y	手势事件y方向的位移值, 向上移动时为负数
angle			rotate事件触发时旋转的角度
duration		touchstart 与 touchend之间的时间戳
factor			swipe事件加速度因子
startRotate		启动单指旋转方法，在某个元素的touchstart触发时调用


 */